﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using RhythmComet.Utils;
using RhythmComet.Services;
using RhythmComet.Particles;
using FMOD;

namespace RhythmComet.Gameplay
{
    public class ObstacleManager : DrawableGameComponent
    {
        RhythmCometGame game;
        private int totalOfObstacles;
        private int lastNotActive;
        float verticalLimit = 250.0f;
        float horizontalLimit = 370.0f;
        private Obstacle[] obstacles;
        private const float spawnTime = 0.25f;
        private float timeSinceLastSpawn = 0.0f;
        private Comet comet;
        VibrationManager vibration;
        ParticleSystemManager psManager;
        IBeatManager beatManager;

        ObstacleSettings[] bandSettings;

        private BasicEffect effect;

        private Color[] listOfColors;

        private int nextSubBassRespawn = 0;

        private Vector3[] positionSubBass = new Vector3[4] { 
            ScreenHelper.BottomLeft, ScreenHelper.BottomRight, ScreenHelper.TopRight, ScreenHelper.TopLeft
        };

        private Vector3[] velocitySubBass = new Vector3[4] {
            ScreenHelper.TopRightDiagonal, ScreenHelper.TopLeftDiagonal, ScreenHelper.DownLeftDiagonal,
            ScreenHelper.DownRightDiagonal 
        };

        private Vector3[] positionTreble = new Vector3[2] { 
            ScreenHelper.CenterLeft, ScreenHelper.CenterRight
        };

        private Vector3[] velocityTreble = new Vector3[6] {
            ScreenHelper.CenterLeftToCenterTop, ScreenHelper.CenterLeftToCenterRight, ScreenHelper.CenterLeftToCenterBotton,
            ScreenHelper.CenterRightToCenterTop, ScreenHelper.CenterRightToCenterLeft, ScreenHelper.CenterRightToCenterBotton
        };

        private Vector3 lastPositionUpperMiderange;
        private Vector3 lastPositionLowerMidrange;

        public Obstacle[] Obstacles
        {
            get { return obstacles; }
        }

        public ObstacleManager(Game game, int total, Comet comet)
            : base(game)
        {
            this.game = (RhythmCometGame) game;
            totalOfObstacles = total;
            listOfColors = new Color[4] { Color.Blue, Color.Green, Color.Yellow, Color.Red };
            this.comet = comet;
        }

        public override void Initialize()
        {
            effect = new BasicEffect(game.GraphicsDevice, null);

            effect.View = game.Camera.View;
            effect.Projection = game.Camera.Projection;

            obstacles = new Obstacle[totalOfObstacles];
            lastNotActive = 0;
            for (int i = 0; i < obstacles.Length; i++)
                obstacles[i] = new Obstacle();

            vibration = new VibrationManager(this.game, 300);
            game.Components.Add(vibration);

            psManager = new ParticleSystemManager(this.game, 100);


            this.bandSettings = new ObstacleSettings[7];

            this.bandSettings[0] = new ObstacleSettings();
            this.bandSettings[0].bandType = SubBands.SubBass;
            this.bandSettings[0].minVerticalVelocity = 1.0f;
            this.bandSettings[0].maxVerticalVelocity = 1.2f;
            this.bandSettings[0].minHorizontalVelocity = 1.0f;
            this.bandSettings[0].maxHorizontalVelocity = 1.2f;
            this.bandSettings[0].minSize = 35.0f;
            this.bandSettings[0].maxSize = 40.0f;
            this.bandSettings[0].minHorizontalPosition = 0.0f;
            this.bandSettings[0].maxHorizontalPosition = 0.0f;
            this.bandSettings[0].maxVerticalVelocity = 0.0f;
            this.bandSettings[0].minVerticalVelocity = 0.0f;

            this.bandSettings[1] = new ObstacleSettings();
            this.bandSettings[1].bandType = SubBands.Bass;
            this.bandSettings[1].minVerticalVelocity = 1.0f;
            this.bandSettings[1].maxVerticalVelocity = 1.2f;
            this.bandSettings[1].minHorizontalVelocity = 1.0f;
            this.bandSettings[1].maxHorizontalVelocity = 1.2f;
            this.bandSettings[1].minSize = 10.0f;
            this.bandSettings[1].maxSize = 15.0f;
            this.bandSettings[1].minHorizontalPosition = 0.0f;
            this.bandSettings[1].maxHorizontalPosition = 0.0f;
            this.bandSettings[1].maxVerticalVelocity = 0.0f;
            this.bandSettings[1].minVerticalVelocity = 0.0f;


            this.bandSettings[2] = new ObstacleSettings();
            this.bandSettings[2].bandType = SubBands.Treble;
            this.bandSettings[2].minVerticalVelocity = 2.5f;
            this.bandSettings[2].maxVerticalVelocity = 3.0f;
            this.bandSettings[2].minHorizontalVelocity = 1.5f;
            this.bandSettings[2].maxHorizontalVelocity = 1.5f;
            this.bandSettings[2].minSize = 10.0f;
            this.bandSettings[2].maxSize = 10.0f;
            this.bandSettings[2].minHorizontalPosition = 0.0f;
            this.bandSettings[2].maxHorizontalPosition = 0.0f;
            this.bandSettings[2].maxVerticalVelocity = 0.0f;
            this.bandSettings[2].minVerticalVelocity = 0.0f;

            this.bandSettings[3] = new ObstacleSettings();
            this.bandSettings[3].bandType = SubBands.Presence;
            this.bandSettings[3].minVerticalVelocity = 1.5f;
            this.bandSettings[3].maxVerticalVelocity = 1.5f;
            this.bandSettings[3].minHorizontalVelocity = 1.5f;
            this.bandSettings[3].maxHorizontalVelocity = 1.5f;
            this.bandSettings[3].minSize = 15.0f;
            this.bandSettings[3].maxSize = 25.0f;
            this.bandSettings[3].minHorizontalPosition = 0.0f;
            this.bandSettings[3].maxHorizontalPosition = 0.0f;
            this.bandSettings[3].maxVerticalVelocity = 0.0f;
            this.bandSettings[3].minVerticalVelocity = 0.0f;

            this.bandSettings[4] = new ObstacleSettings();
            this.bandSettings[4].bandType = SubBands.LowerMidrange;
            this.bandSettings[4].minVerticalVelocity = 1.5f;
            this.bandSettings[4].maxVerticalVelocity = 1.5f;
            this.bandSettings[4].minHorizontalVelocity = 1.5f;
            this.bandSettings[4].maxHorizontalVelocity = 1.5f;
            this.bandSettings[4].minSize = 40.0f;
            this.bandSettings[4].maxSize = 40.0f;
            this.bandSettings[4].minHorizontalPosition = 0.0f;
            this.bandSettings[4].maxHorizontalPosition = 0.0f;
            this.bandSettings[4].maxVerticalVelocity = 0.0f;
            this.bandSettings[4].minVerticalVelocity = 0.0f;

            this.bandSettings[5] = new ObstacleSettings();
            this.bandSettings[5].bandType = SubBands.UpperMidrange;
            this.bandSettings[5].minVerticalVelocity = 1.5f;
            this.bandSettings[5].maxVerticalVelocity = 1.5f;
            this.bandSettings[5].minHorizontalVelocity = 1.5f;
            this.bandSettings[5].maxHorizontalVelocity = 1.5f;
            this.bandSettings[5].minSize = 40.0f;
            this.bandSettings[5].maxSize = 40.0f;
            this.bandSettings[5].minHorizontalPosition = 0.0f;
            this.bandSettings[5].maxHorizontalPosition = 0.0f;
            this.bandSettings[5].maxVerticalVelocity = 0.0f;
            this.bandSettings[5].minVerticalVelocity = 0.0f;

            this.beatManager = this.game.BeatManager;

            this.lastPositionUpperMiderange = new Vector3(ScreenHelper.TopLeft.X, 
                ScreenHelper.TopLeft.Y, ScreenHelper.TopLeft.Z);

            this.lastPositionLowerMidrange = new Vector3(ScreenHelper.TopRight.X,
                ScreenHelper.TopRight.Y, ScreenHelper.TopRight.Z);

            base.Initialize();
        }

        public override void Draw(GameTime gameTime)
        {
            game.GraphicsDevice.RenderState.DepthBufferEnable = true;
            game.GraphicsDevice.RenderState.AlphaBlendEnable = false;
            game.GraphicsDevice.RenderState.AlphaTestEnable = false;
            game.GraphicsDevice.VertexDeclaration = new VertexDeclaration(game.GraphicsDevice, VertexPositionColor.VertexElements);
            effect.VertexColorEnabled = true;
            effect.Begin();
            for (int i = lastNotActive - 1; i >= 0; i--)
            {
                obstacles[i].Draw(game.GraphicsDevice, effect);
            }
            effect.End();

            psManager.Draw(gameTime);

            base.Draw(gameTime);
        }

        public override void Update(GameTime gameTime)
        {
            CheckCollision(gameTime);
            for (int i = lastNotActive - 1; i >= 0; i--)
            {
                obstacles[i].Update(gameTime);
                if ((!obstacles[i].Active) ||
                    (obstacles[i].Velocity.X > 0.0f && obstacles[i].CenterPosition.X > horizontalLimit) ||
                    (obstacles[i].Velocity.X < 0.0f && obstacles[i].CenterPosition.X < -horizontalLimit) ||
                    (obstacles[i].Velocity.Y > 0.0f && obstacles[i].CenterPosition.Y > verticalLimit) ||
                    (obstacles[i].Velocity.Y < 0.0f && obstacles[i].CenterPosition.Y < -verticalLimit))
                {
                    RemoveObstacle(i);
                }
            }
            timeSinceLastSpawn += (float)gameTime.ElapsedGameTime.TotalSeconds;

            SubBands band = beatManager.LouderBand;
            if (band != SubBands.None && beatManager.Band[(int)band].Beat && beatManager.Band[(int)band].OkToSpawn(gameTime))
            {
                AddObstacle(band);
            }
            // mudanca de ultima hora
            if (band != SubBands.None && beatManager.Band[(int)SubBands.Treble].Beat && beatManager.Band[(int)SubBands.Treble].OkToSpawn(gameTime))
            {
                AddObstacle(SubBands.Treble);
            }

            if (band != SubBands.None && beatManager.Band[(int)SubBands.Presence].Beat && beatManager.Band[(int)SubBands.Presence].OkToSpawn(gameTime))
            {
                AddObstacle(SubBands.Presence);
            }

            psManager.Update(gameTime);

            base.Update(gameTime);
        }

        public void AddObstacle(SubBands band)
        {
            switch (band)
            {
                case SubBands.SubBass :
                    //AddObstacleSubBass();
                    break;
                case SubBands.LowBass:
                    //AddObstacleSubBass();
                    break;
                case SubBands.Bass :
                    //AddObstacleSubBass();
                    break;
                case SubBands.LowerMidrange :
                    AddObstacleLowerMidrange();
                    break;
                case SubBands.UpperMidrange :
                    AddObstacleUpperMidrange();
                    break;
                case SubBands.Presence :
                    AddObstaclePresence();
                    break;
                case SubBands.Treble :
                    AddObstacleTreble();
                    break;
                default :
                    break;
            }
        }

        public void AddObstacleBass()
        {
            ObstacleSettings settings = bandSettings[(int)SubBands.Bass];
            float size = RandomGenerator.RandomBetween(settings.minSize, settings.maxSize);
            Shape shape = Shape.Triangle;
            float rotation = MathHelper.Pi / 72.0f;

            Color color = listOfColors[RandomGenerator.RandomBetween(0, 3)];

            Vector3 position = positionSubBass[nextSubBassRespawn];
            Vector3 velocity = velocitySubBass[nextSubBassRespawn];

            obstacles[lastNotActive++].Initialize(shape, color,
                new Vector2(position.X, position.Y), size,
                new Vector2(velocity.X, velocity.Y), rotation);

            //nextSubBassRespawn = (nextSubBassRespawn + 1) % 4;
        }

        public void AddObstacleSubBass()
        {
            ObstacleSettings settings = bandSettings[(int)SubBands.SubBass];
            float size = RandomGenerator.RandomBetween(settings.minSize, settings.maxSize);
            Shape shape = Shape.Square;
            float rotation = MathHelper.Pi / 72.0f;

            Color color = listOfColors[RandomGenerator.RandomBetween(0, 3)];

            Vector3 position = positionSubBass[nextSubBassRespawn];
            Vector3 velocity = velocitySubBass[nextSubBassRespawn];

            obstacles[lastNotActive++].Initialize(shape, color,
                new Vector2(position.X, position.Y), size,
                new Vector2(velocity.X, velocity.Y), rotation);

            //nextSubBassRespawn = (nextSubBassRespawn + 1) % 4;
        }

        public void AddObstacleTreble()
        {
            ObstacleSettings settings = bandSettings[2];
            float size = RandomGenerator.RandomBetween(settings.minSize, settings.maxSize);
            Shape shape = Shape.Triangle;//(Shape)RandomGenerator.RandomBetween(0, 3);
            float rotation = MathHelper.Pi / 72.0f;

            Color color = listOfColors[RandomGenerator.RandomBetween(0, 3)];
            float velocityFactor = 2.0f;
            for (int i = 0; i < 6; i++)
            {
                Vector3 position = positionTreble[i / 3];
                Vector3 velocity = velocityTreble[i] * velocityFactor;

                obstacles[lastNotActive++].Initialize(shape, color,
                    new Vector2(position.X, position.Y), size,
                    new Vector2(velocity.X, velocity.Y), rotation);
                game.Score.AddObstacleColor(color);
            }

            //nextSubBassRespawn = (nextSubBassRespawn + 1) % 4;
        }

        public void AddObstaclePresence()
        {
            ObstacleSettings settings = bandSettings[3];
            float size = RandomGenerator.RandomBetween(settings.minSize, settings.maxSize);
            Shape shape = Shape.Diamond;//(Shape)RandomGenerator.RandomBetween(0, 3);
            float rotation = MathHelper.Pi / 72.0f;

            Color color = listOfColors[RandomGenerator.RandomBetween(0, 3)];

            Vector3 position = positionSubBass[nextSubBassRespawn];
            Vector3 velocity = velocitySubBass[nextSubBassRespawn];
            
            obstacles[lastNotActive++].Initialize(shape, color,
                new Vector2(position.X, position.Y), size,
                new Vector2(velocity.X, velocity.Y), rotation);

            game.Score.AddObstacleColor(color);
            nextSubBassRespawn = (nextSubBassRespawn + 1) % 4;
        }

        public void AddObstacleUpperMidrange()
        {
            ObstacleSettings settings = bandSettings[4];
            float size = RandomGenerator.RandomBetween(settings.minSize, settings.maxSize);
            Shape shape = Shape.Square;//(Shape)RandomGenerator.RandomBetween(0, 3);
            float rotation = RandomGenerator.RandomBetween(MathHelper.Pi / 72.0f, MathHelper.Pi / 36.0f);

            Color color = listOfColors[RandomGenerator.RandomBetween(0, 3)];

            //float yOffset = RandomGenerator.RandomBetween(ScreenHelper.BottomLeft.Y, ScreenHelper.TopLeft.Y);
            float velocityFactor = RandomGenerator.RandomBetween(1.0f, 1.2f);
            Vector3 position = this.lastPositionUpperMiderange;
            Vector3 velocity;

            this.lastPositionUpperMiderange.X *= -1;
            this.lastPositionUpperMiderange.Y *= -1;

            if (position.X < 0)
            {
                velocity = Vector3.Right * velocityFactor;
                
            }
            else
            {
                velocity = Vector3.Left * velocityFactor;
                this.lastPositionUpperMiderange.Y -= 30.0f;
                if (this.lastPositionUpperMiderange.Y < -ScreenHelper.VerticalLimit)
                {
                    this.lastPositionUpperMiderange.Y = ScreenHelper.TopLeft.Y;
                }
            }
            

            //if (RandomGenerator.Random.NextDouble() < 0.5f)
            //{
            //    position = ScreenHelper.CenterLeft;
            //    velocity = Vector3.Right * velocityFactor;
            //}
            //else
            //{
            //    position = ScreenHelper.CenterRight;
            //    velocity = Vector3.Left * velocityFactor;
            //}

            obstacles[lastNotActive++].Initialize(shape, color,
                new Vector2(position.X, position.Y), size,
                new Vector2(velocity.X, velocity.Y), rotation);

            game.Score.AddObstacleColor(color);
        }

        public void AddObstacleLowerMidrange()
        {
            ObstacleSettings settings = bandSettings[5];
            float size = RandomGenerator.RandomBetween(settings.minSize, settings.maxSize);
            Shape shape = Shape.Hexagon;//(Shape)RandomGenerator.RandomBetween(0, 3);
            float rotation = RandomGenerator.RandomBetween(MathHelper.Pi / 72.0f, MathHelper.Pi / 36.0f);

            Color color = listOfColors[RandomGenerator.RandomBetween(0, 3)];

            //float xOffset = RandomGenerator.RandomBetween(ScreenHelper.BottomLeft.X, ScreenHelper.BottomRight.X);
            float velocityFactor = RandomGenerator.RandomBetween(1.0f, 1.2f);
            Vector3 position = this.lastPositionLowerMidrange;
            Vector3 velocity;

            this.lastPositionLowerMidrange.X *= -1;
            this.lastPositionLowerMidrange.Y *= -1;

            if (position.Y > 0)
            {
                velocity = Vector3.Down * velocityFactor;

            }
            else
            {
                velocity = Vector3.Up * velocityFactor;
                this.lastPositionLowerMidrange.X -= 30.0f;
                if (this.lastPositionLowerMidrange.X < -ScreenHelper.HorizontalLimit)
                {
                    this.lastPositionLowerMidrange.X = ScreenHelper.TopRight.X;
                }
            }

            //if (RandomGenerator.Random.NextDouble() < 0.5f)
            //{
            //    position = ScreenHelper.CenterTop;
            //    velocity = Vector3.Down * velocityFactor;
            //}
            //else
            //{
            //    position = ScreenHelper.CenterBottom;
            //    velocity = Vector3.Up * velocityFactor;
            //}
            obstacles[lastNotActive++].Initialize(shape, color,
                new Vector2(position.X, position.Y), size,
                new Vector2(velocity.X, velocity.Y), rotation);

            game.Score.AddObstacleColor(color);
        }

        public void RemoveObstacle(int i)
        {
            Obstacle temp;
            temp = obstacles[i];
            obstacles[i] = obstacles[lastNotActive - 1];
            obstacles[--lastNotActive] = temp;
            obstacles[lastNotActive].Active = false;
        }

        private void CheckCollision(GameTime gameTime)
        {
            bool collided;
            foreach (Obstacle obstacle in this.obstacles)
            {
                collided = false;
                if (obstacle.Active && comet.Bound.Intersects(obstacle.Bound))
                {
                    Vector3 cometPosition = new Vector3(comet.Location, 0.0f);
                    for (int j = 1; j < obstacle.TotalLines; j++)
                    {
                        if (CollisionHelper.LineCircleCollision(obstacle.Vertices[j - 1].Position, obstacle.Vertices[j].Position,
                            cometPosition, 5.0f))
                        {
                            collided = true;
                        }
                        if (!collided && obstacle.Polygon.Contains(cometPosition))
                        {
                            collided = true;
                        }
                        if (collided)
                        {
                            this.psManager.Add(CreateExplosion(obstacle));
                            obstacle.Active = false;

                            if (comet.Color == obstacle.Color)
                            {
                                
                                game.Score.IncreaseScore(obstacle.Color);
                            }
                            else
                            {
                                vibration.Start();
                                game.Score.Faults++;
                                game.Score.DecreaseScore(comet.Color);
                            }
                            break;
                        }
                    }
                }
            }
        }

        public void Reset()
        {
            for (int i = lastNotActive - 1; i >= 0; i--)
            {
                RemoveObstacle(i);
            }
        }

        private ParticleSystem CreateExplosion(Obstacle obstacle)
        {
            Vector2 position = new Vector2(obstacle.CenterPosition.X, obstacle.CenterPosition.Y);
            ParticleSystemSettings settings = new ParticleSystemSettings();
            settings.textureFilename = @"sprites\cometSprite";
            settings.minInitialSpeed = 30;
            settings.maxInitialSpeed = 60;
            settings.minAcceleration = 0;
            settings.maxAcceleration = 0;
            settings.minLifetime = 4.0f;
            settings.maxLifetime = 2.0f;
            settings.minScale = 2.0f;
            settings.maxScale = 2.0f;
            settings.minNumParticles = 30;
            settings.maxNumParticles = 30;

            ParticleSystem ps = new ParticleSystem(game, 30, settings);
            ps.Initialize();
            ps.CurrentColor = obstacle.Color;
            ps.AddParticles(position);

            return ps;
        }
    }
}
